package com.psychologyCommunity.Community.service;

import com.psychologyCommunity.Community.util.RedisKeyUtil;
import org.omg.CORBA.PUBLIC_MEMBER;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

@Service
public class DataService {
    @Autowired
    private RedisTemplate redisTemplate;

    //与日期相关，先定义一个日期的格式
    private SimpleDateFormat df=new SimpleDateFormat("yyyyMMdd");

    //两个方面：一是记 我要把这个数据记录下来，每一次请求中 我要截获这个请求，把相关数据就记录 存到redis
    //二是查  当我想看的时候，能提供一个查询的方法 能够访问到

    //将指定的IP计入UV
    public void recordUV(String ip){
        String uvKey = RedisKeyUtil.getUVKey(df.format(new Date()));
        redisTemplate.opsForHyperLogLog().add(uvKey,ip);//存入构建好的key和值
    }
    //统计指定日期范围内的UV
    public long calculateUV(Date start,Date end){
        if (start==null ||end==null){
            throw  new IllegalArgumentException("参数不能为空啊 宝~");
        }
        //整理 该日期范围内的key--此时要定义一个新的空间来存储
        List<String> keyList=new ArrayList<>();
        Calendar calendar=Calendar.getInstance();
        calendar.setTime(start);
        while (!calendar.getTime().after(end)){//当小于等于end结束时间的时候进入循环
            String uvKey = RedisKeyUtil.getUVKey(df.format(calendar.getTime()));//这就是其中一个key
            keyList.add(uvKey);//把当前拿到的key放到List中
            calendar.add(Calendar.DATE,1);//当前的日期加1 往后挪一天
        }
        //合并这些数据 的key也要放到一个新的 redisKey中
        String uvKeys = RedisKeyUtil.getDuringUVKey(df.format(start), df.format(end));
        redisTemplate.opsForHyperLogLog().union(uvKeys,keyList.toArray());//合并之前存放到keyList，就是一天一个ip访问n次都只有一条记录

        //返回统计的结果
        return  redisTemplate.opsForHyperLogLog().size(uvKeys);
    }
    //将指定用户计入DAU
    public void recordDAU(int userId){
        String dauKey=RedisKeyUtil.getDAUKey(df.format(new Date()));
        //调用这个方法 肯定是说明这个用户是活跃的 即为true，没有调用就是默认false
        redisTemplate.opsForValue().setBit(dauKey,userId,true);//以setBit方法存入redis，以userId这个整数为索引，存的是布尔值
    }
    //统计指定日期范围内的DAU
    public long calculateDAN(Date start,Date end){
        //和UV类似 ，但是不是合并 而是OR运算 比如说这个用户在七天之内 任何一天登录过一次 就算是活跃用户
        if (start==null ||end==null){
            throw  new IllegalArgumentException("参数不能为空啊 宝~"); }
        //整理 该日期范围内的key--此时要定义一个新的空间来存储
        List<byte[]> keyList=new ArrayList<>();
        Calendar calendar=Calendar.getInstance();
        calendar.setTime(start);
        while (!calendar.getTime().after(end)){//当小于等于end结束时间的时候进入循环
            String uvKey = RedisKeyUtil.getDAUKey(df.format(calendar.getTime()));//这就是其中一个key
            keyList.add(uvKey.getBytes());//把当前拿到的key转成byte数组传入放到List中
            calendar.add(Calendar.DATE,1);//当前的日期加1 往后挪一天
        }

        //进行OR运算
        return (long) redisTemplate.execute(new RedisCallback() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                //拿到这个区间的DAUkey 然后做OR运算
                String dauKey = RedisKeyUtil.getDAUKey(df.format(start), df.format(end));
                connection.bitOp(RedisStringCommands.BitOperation.OR,
                        dauKey.getBytes(),keyList.toArray(new byte[0][0]));
                return connection.bitCount(dauKey.getBytes());
            }
        });
    }
}
